/*
 * Copyright (C) 2025 SDFX Project
 * SDFX is licensed under Mulan PSL v2.
 */

/**
 * @file sdf_asymmetric.c
 * @brief SDF asymmetric cryptography algorithm module
 */

#include "sdf_internal.h"

/* Module Initialization */
LONG sdf_asymmetric_init(void)
{
    return SDR_OK;
}

/* Module Cleanup */
void sdf_asymmetric_cleanup(void)
{
    /* Clean up asymmetric encryption related resources */
}

LONG SDF_InternalSign_ECC(HANDLE hSessionHandle, ULONG uiISKIndex, 
    BYTE *pucData, ULONG uiDataLength, ECCSignature *pucSignature)
{
    // TODO: Implement ECC internal signing
    SDF_CHECK_PARAM(hSessionHandle != NULL && pucData != NULL && 
                    uiDataLength > 0 && pucSignature != NULL, SDR_INARGERR);
    
    return SDR_NOTSUPPORT;  // Not yet implemented
}

LONG SDF_InternalVerify_ECC(HANDLE hSessionHandle, ULONG uiISKIndex, 
    BYTE *pucData, ULONG uiDataLength, ECCSignature *pucSignature)
{
    // TODO: Implement ECC internal verification
    SDF_CHECK_PARAM(hSessionHandle != NULL && pucData != NULL && 
                    uiDataLength > 0 && pucSignature != NULL, SDR_INARGERR);
    
    return SDR_NOTSUPPORT;  // Not yet implemented
}

LONG SDF_ExternalEncrypt_ECC(HANDLE hSessionHandle, ULONG uiAlgID, 
    ECCrefPublicKey *pucPublicKey, BYTE *pucData, ULONG uiDataLength, 
    ECCCipher *pucEncData)
{
    SDF_CHECK_PARAM(hSessionHandle != NULL && pucPublicKey != NULL && 
                    pucData != NULL && uiDataLength > 0 && 
                    pucEncData != NULL, SDR_INARGERR);
    
    HANDLE server_session_id;
    SDF_CHECK_SESSION(hSessionHandle, server_session_id);
    
    sdfx_external_encrypt_ecc_req_t *req;
    BYTE resp_buffer[1024];
    size_t req_size = sizeof(sdfx_external_encrypt_ecc_req_t) + uiDataLength;
    size_t resp_len;
    sdfx_message_t *resp_msg;
    LONG ret;
    
    req = malloc(req_size);
    if (req == NULL) {
        return SDR_MEMORY_ERROR;
    }
    
    req->session_handle = server_session_id;
    req->alg_id = sdfx_htonl(uiAlgID);
    memcpy(&req->public_key, pucPublicKey, sizeof(ECCrefPublicKey));
    req->data_length = sdfx_htonl(uiDataLength);
    memcpy(req->data, pucData, uiDataLength);
    
    ret = sdf_send_request(SDFX_CMD_EXTERNAL_ENCRYPT_ECC, req, req_size,
                           resp_buffer, sizeof(resp_buffer), &resp_len);
    
    if (ret == SDR_OK) {
        /* Parse response */
        resp_msg = (sdfx_message_t*)resp_buffer;
        if (resp_msg->header.length >= sizeof(sdfx_external_encrypt_ecc_resp_t)) {
            sdfx_external_encrypt_ecc_resp_t *resp = (sdfx_external_encrypt_ecc_resp_t*)resp_msg->data;
            memcpy(pucEncData, &resp->cipher, sizeof(ECCCipher));
            /* Convert byte order */
            pucEncData->L = sdfx_ntohl(pucEncData->L);
        } else {
            ret = SDR_PROTOCOL_ERROR;
        }
    }
    
    free(req);
    return ret;
}

LONG SDF_ExternalDecrypt_ECC(HANDLE hSessionHandle, ULONG uiAlgID, 
    ECCrefPrivateKey *pucPrivateKey, ECCCipher *pucEncData, 
    BYTE *pucData, ULONG *puiDataLength)
{
    SDF_CHECK_PARAM(hSessionHandle != NULL && pucPrivateKey != NULL && 
                    pucEncData != NULL && pucData != NULL && 
                    puiDataLength != NULL, SDR_INARGERR);
    
    HANDLE server_session_id;
    SDF_CHECK_SESSION(hSessionHandle, server_session_id);
    
    sdfx_external_decrypt_ecc_req_t req;
    BYTE resp_buffer[1024];
    size_t resp_len;
    sdfx_message_t *resp_msg;
    LONG ret;
    
    req.session_handle = server_session_id;
    req.alg_id = sdfx_htonl(uiAlgID);
    memcpy(&req.private_key, pucPrivateKey, sizeof(ECCrefPrivateKey));
    /* Copy cipher structure and convert byte order */
    memcpy(&req.cipher, pucEncData, sizeof(ECCCipher));
    req.cipher.L = sdfx_htonl(req.cipher.L);
    
    ret = sdf_send_request(SDFX_CMD_EXTERNAL_DECRYPT_ECC, &req, sizeof(req),
                           resp_buffer, sizeof(resp_buffer), &resp_len);
    if (ret != SDR_OK) {
        return ret;
    }
    
    /* Parse response */
    resp_msg = (sdfx_message_t*)resp_buffer;
    if (resp_msg->header.length >= sizeof(sdfx_external_decrypt_ecc_resp_t)) {
        sdfx_external_decrypt_ecc_resp_t *resp = (sdfx_external_decrypt_ecc_resp_t*)resp_msg->data;
        ULONG plaintext_len = sdfx_ntohl(resp->data_length);
        if (plaintext_len > *puiDataLength) {
            return SDR_MEMORY_ERROR;
        }
        
        memcpy(pucData, resp->data, plaintext_len);
        *puiDataLength = plaintext_len;
        
        return SDR_OK;
    }
    
    return SDR_PROTOCOL_ERROR;
}

LONG SDF_GenerateKeyPair_ECC(HANDLE hSessionHandle, ULONG uiAlgID, ULONG uiKeyBits,
    ECCrefPublicKey *pucPublicKey, ECCrefPrivateKey *pucPrivateKey)
{
    SDF_CHECK_PARAM(hSessionHandle != NULL && pucPublicKey != NULL && 
                    pucPrivateKey != NULL && uiKeyBits == 256, SDR_INARGERR);
    
    HANDLE server_session_id;
    SDF_CHECK_SESSION(hSessionHandle, server_session_id);
    
    sdfx_generate_keypair_ecc_req_t req;
    BYTE resp_buffer[1024];
    size_t resp_len;
    sdfx_message_t *resp_msg;
    LONG ret;
    
    req.session_handle = server_session_id;
    req.alg_id = sdfx_htonl(uiAlgID);
    
    ret = sdf_send_request(SDFX_CMD_GENERATE_KEYPAIR_ECC, &req, sizeof(req),
                           resp_buffer, sizeof(resp_buffer), &resp_len);
    if (ret != SDR_OK) {
        return ret;
    }
    
    /* Parse response */
    resp_msg = (sdfx_message_t*)resp_buffer;
    if (resp_msg->header.length >= sizeof(sdfx_generate_keypair_ecc_resp_t)) {
        sdfx_generate_keypair_ecc_resp_t *resp = (sdfx_generate_keypair_ecc_resp_t*)resp_msg->data;
        
        memcpy(pucPublicKey, &resp->public_key, sizeof(ECCrefPublicKey));
        memcpy(pucPrivateKey, &resp->private_key, sizeof(ECCrefPrivateKey));
        
        return SDR_OK;
    }
    
    return SDR_PROTOCOL_ERROR;
}

LONG SDF_ExternalSign_ECC(HANDLE hSessionHandle, ULONG uiAlgID,
    ECCrefPrivateKey *pucPrivateKey, BYTE *pucData, ULONG uiDataLength,
    ECCSignature *pucSignature)
{
    SDF_CHECK_PARAM(hSessionHandle != NULL && pucPrivateKey != NULL && 
                    pucData != NULL && uiDataLength > 0 && 
                    pucSignature != NULL, SDR_INARGERR);
    
    HANDLE server_session_id;
    SDF_CHECK_SESSION(hSessionHandle, server_session_id);
    
    sdfx_external_sign_ecc_req_t *req;
    BYTE resp_buffer[1024];
    size_t req_size = sizeof(sdfx_external_sign_ecc_req_t) + uiDataLength;
    size_t resp_len;
    sdfx_message_t *resp_msg;
    LONG ret;
    
    req = malloc(req_size);
    if (req == NULL) {
        return SDR_MEMORY_ERROR;
    }
    
    req->session_handle = server_session_id;
    req->alg_id = sdfx_htonl(uiAlgID);
    memcpy(&req->private_key, pucPrivateKey, sizeof(ECCrefPrivateKey));
    req->data_length = sdfx_htonl(uiDataLength);
    memcpy(req->data, pucData, uiDataLength);
    
    ret = sdf_send_request(SDFX_CMD_EXTERNAL_SIGN_ECC, req, req_size,
                           resp_buffer, sizeof(resp_buffer), &resp_len);
    
    if (ret == SDR_OK) {
        /* Parse response */
        resp_msg = (sdfx_message_t*)resp_buffer;
        if (resp_msg->header.length >= sizeof(sdfx_external_sign_ecc_resp_t)) {
            sdfx_external_sign_ecc_resp_t *resp = (sdfx_external_sign_ecc_resp_t*)resp_msg->data;
            ULONG sig_len = sdfx_ntohl(resp->signature_length);
            if (sig_len == 64) { /* SM2 signature format: r(32) + s(32) */
                memcpy(pucSignature->r, resp->signature, 32);
                memcpy(pucSignature->s, resp->signature + 32, 32);
            } else {
                ret = SDR_KEYERR;
            }
        } else {
            ret = SDR_PROTOCOL_ERROR;
        }
    }
    
    free(req);
    return ret;
}

LONG SDF_ExternalVerify_ECC(HANDLE hSessionHandle, ULONG uiAlgID,
    ECCrefPublicKey *pucPublicKey, BYTE *pucData, ULONG uiDataLength,
    ECCSignature *pucSignature)
{
    SDF_CHECK_PARAM(hSessionHandle != NULL && pucPublicKey != NULL && 
                    pucData != NULL && uiDataLength > 0 && 
                    pucSignature != NULL, SDR_INARGERR);
    
    HANDLE server_session_id;
    SDF_CHECK_SESSION(hSessionHandle, server_session_id);
    
    sdfx_external_verify_ecc_req_t *req;
    BYTE resp_buffer[1024];
    size_t req_size = sizeof(sdfx_external_verify_ecc_req_t) + uiDataLength + 64;
    size_t resp_len;
    sdfx_message_t *resp_msg;
    LONG ret;
    
    req = malloc(req_size);
    if (req == NULL) {
        return SDR_MEMORY_ERROR;
    }
    
    req->session_handle = server_session_id;
    req->alg_id = sdfx_htonl(uiAlgID);
    memcpy(&req->public_key, pucPublicKey, sizeof(ECCrefPublicKey));
    req->data_length = sdfx_htonl(uiDataLength);
    req->signature_length = sdfx_htonl(64); /* SM2 signature length */
    
    /* Copy data and signature */
    memcpy(req->payload, pucData, uiDataLength);
    memcpy(req->payload + uiDataLength, pucSignature->r, 32);
    memcpy(req->payload + uiDataLength + 32, pucSignature->s, 32);
    
    ret = sdf_send_request(SDFX_CMD_EXTERNAL_VERIFY_ECC, req, req_size,
                           resp_buffer, sizeof(resp_buffer), &resp_len);
    
    if (ret == SDR_OK) {
        /* Parse response */
        resp_msg = (sdfx_message_t*)resp_buffer;
        if (resp_msg->header.length >= sizeof(sdfx_external_verify_ecc_resp_t)) {
            sdfx_external_verify_ecc_resp_t *resp = (sdfx_external_verify_ecc_resp_t*)resp_msg->data;
            ULONG result = sdfx_ntohl(resp->result);
            ret = (result == 0) ? SDR_OK : SDR_VERIFYERR;
        } else {
            ret = SDR_PROTOCOL_ERROR;
        }
    }
    
    free(req);
    return ret;
}